﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.IO;
using System.Web.Mvc;

using Domzs.Extensions;

namespace System.Web.Mvc
{
    public static class HtmlTreeRendererHelperExtension
    {
        /*
        public ActionResult Index()
        {
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            CompositeThing com = new CompositeThing();
            List<CompositeThing> test = new List<CompositeThing>();

            CompositeThing child1 = new CompositeThing();
            child1.Name = "C1";

            CompositeThing child2 = new CompositeThing();
            child2.Name = "C2";
            test.Add(child1);
            test.Add(child2);

            com.Children = test;

            CompositeThing Parent = new CompositeThing();
            Parent.Name = "Main";
            com.Parent = Parent;

            return View(com);
        }
        */

        /// <summary>
        /// Usage: <%= Html.RenderTree((CompositeThing)ViewData.Model, thing => thing.Name) %> 
        /// Html.RenderTree<CompositeThing>((CompositeThing)ViewData.Model, thing => thing.Name)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="htmlHelper"></param>
        /// <param name="rootLocations"></param>
        /// <param name="locationRenderer"></param>
        /// <returns></returns>
        public static string RenderTree<T>(this HtmlHelper htmlHelper, IEnumerable<T> rootLocations, Func<T, string> locationRenderer) where T : IComposite<T>
        {
            return new TreeRenderer<T>(rootLocations, locationRenderer).Render();
        }
    }

    public class TreeRenderer<T> where T : IComposite<T>
    {
        private readonly Func<T, string> locationRenderer;
        private readonly IEnumerable<T> rootLocations;
        private HtmlTextWriter writer;
        public TreeRenderer(IEnumerable<T> rootLocations, Func<T, string> locationRenderer)
        {
            this.rootLocations = rootLocations;
            this.locationRenderer = locationRenderer;
        }

        public string Render()
        {
            writer = new HtmlTextWriter(new StringWriter());
            RenderLocations(rootLocations);
            return writer.InnerWriter.ToString();
        }

        /// <summary>
        /// Recursively walks the location tree outputting it as hierarchical UL/LI elements
        /// </summary>
        /// <param name="locations"></param>
        private void RenderLocations(IEnumerable<T> locations)
        {
            if (locations == null) return;
            if (locations.Count() == 0) return;

            InUl(() => locations.ForEach(location => InLi(() =>
            {
                writer.Write(locationRenderer(location));
                RenderLocations(location.Children);

            })));
        }

        private void InUl(Action action)
        {
            writer.WriteLine();
            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            action();
            writer.RenderEndTag();
            writer.WriteLine();
        }

        private void InLi(Action action)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Li);
            action();
            writer.RenderEndTag();
            writer.WriteLine();
        }
    }
}
